package com.b511.swpu.cementbackend.service.impl;

import com.b511.swpu.cementbackend.service.MetaDictService;
import com.b511.swpu.cementbackend.dto.input.MetaDictInputDto;
import com.b511.swpu.cementbackend.dto.output.MetaDictOutputDto;
import com.b511.swpu.cementbackend.entity.MetaDict;
import com.b511.swpu.cementbackend.exception.CementException;
import com.b511.swpu.cementbackend.repository.MetaDictRepository;
import com.b511.swpu.cementbackend.util.enumeration.Code;
import com.b511.swpu.cementbackend.util.CodeUtil;
import com.b511.swpu.cementbackend.util.R;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
import java.util.stream.Stream;

@Service
public class MetaDictServiceImpl implements MetaDictService {

    private final MetaDictRepository metaDictRepository;

    private final CodeUtil codeUtil;

    public MetaDictServiceImpl(MetaDictRepository metaDictRepository, CodeUtil codeUtil) {
        this.metaDictRepository = metaDictRepository;
        this.codeUtil = codeUtil;
    }

    @Override
    @Transactional
    public R addDict(MetaDictInputDto metaDictInputDto) {
        String maxLevel1Code = metaDictRepository.findMaxLevel1Code(MetaDict.DICT_LEVEL_LENGTH);
        String nextCode = null == maxLevel1Code ? codeUtil.initCode(MetaDict.DICT_LEVEL_LENGTH) : codeUtil.next(maxLevel1Code);
        return addDictProcess(nextCode, metaDictInputDto);
    }


    @Override
    @Transactional
    public R addDictChild(String parentCode, MetaDictInputDto metaDictInputDto) {
        String maxLevel2Code = metaDictRepository.findMaxLevel2Code(MetaDict.DICT_LEVEL_LENGTH, parentCode);
        String nextCode = maxLevel2Code.equals(parentCode) ? parentCode + codeUtil.initCode(MetaDict.DICT_LEVEL_LENGTH) : codeUtil.next(maxLevel2Code);
        return addDictProcess(nextCode, metaDictInputDto);
    }

    private R addDictProcess(String nextCode, MetaDictInputDto metaDictInputDto) {
        MetaDict metaDict = new MetaDict();
        metaDict.setCode(nextCode);
        BeanUtils.copyProperties(metaDictInputDto, metaDict);
        metaDictRepository.save(metaDict);
        return R.ok();
    }

    @Override
    @Transactional
    public R getDict() {
        TreeSet<MetaDictOutputDto> data = new TreeSet<>();
        Stream<MetaDict> parentDict = metaDictRepository.findParentDict(MetaDict.DICT_LEVEL_LENGTH);
        Map<String, String> parentMap = new HashMap<>();
        parentDict.forEach((parent) -> parentMap.put(parent.getCode(), parent.getLabel()));
        Stream<MetaDict> childDict = metaDictRepository.findParentDict(MetaDict.DICT_LEVEL_LENGTH * 2);
        childDict.forEach((child -> {
            String parentCode = child.getCode().substring(0, MetaDict.DICT_LEVEL_LENGTH);
            data.add(MetaDictOutputDto.builder()
                    .parentCode(parentCode)
                    .parentName(parentMap.get(parentCode))
                    .code(child.getCode())
                    .name(child.getLabel()).build());
        }));
        return R.ok().putData(data);
    }

    @Override
    public R getDictByName(String name) {
        MetaDict metaDict = metaDictRepository.findByLabel(name);
        if (null == metaDict) {
            throw new CementException(Code.SELECT_FAIL);
        }
        return R.ok().putData(metaDictRepository.findByCodeStartingWithAndLabelNot(metaDict.getCode(), name));
    }
}
